package com.ecloud.common.web.exception;

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;

import com.ecloud.common.base.exception.BusinessException;
import com.ecloud.common.base.result.Prompt;
import com.ecloud.common.base.result.Result;
import com.ecloud.common.base.result.ResultBuilder;
import com.ecloud.common.base.result.ResultStatus;

import feign.FeignException;

/**
 * 全局异常处理器
 *
 * @author LiuGangQiang Create in 2020/03/02
 */
@Order(-99)
@RestController
@ControllerAdvice
@AutoConfigureBefore(ErrorMvcAutoConfiguration.class)
public class AutoGlobalExceptionHandler implements ErrorController {
    private static final Logger LOGGER = LoggerFactory.getLogger(AutoGlobalExceptionHandler.class);

    /**
     * 默认错误处理
     *
     * @author LiuGangQiang Create in 2021/08/02
     * @return {@link Result}
     */
    @RequestMapping(value = { "/error" })
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public Result<?> error(HttpServletRequest request) {
        Locale locale = LocaleContextHolder.getLocale();
        return ResultBuilder.build(ResultStatus.METHOD_NOT_FOUND).setLocale(locale);
    }

    /**
     * 方法不被允许异常处理
     *
     * @author LiuGangQiang Create in 2020/04/20
     * @param e {@link HttpRequestMethodNotSupportedException}
     * @return {@link Result}
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public Result<?> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        if (LOGGER.isWarnEnabled()) {
            LOGGER.warn("method not allowed message --> {}", e.toString());
        }
        return ResultBuilder.build(ResultStatus.METHOD_NOT_ALLOWED).setMsg(e.toString());
    }

    /**
     * 数据校验异常处理(用于表单)
     *
     * @author LiuGangQiang Create in 2020/03/29
     * @param e {@link BindException}
     * @return {@link Result}
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> bindErrorHandler(BindException e) {
        BindingResult result = e.getBindingResult();
        if (LOGGER.isWarnEnabled()) {
            LOGGER.warn("validator message [form] --> {}", result.getFieldError().getDefaultMessage());
        }
        return ResultBuilder.build(ResultStatus.BAD_REQUEST).setMsg(result.getFieldError().getDefaultMessage());
    }

    /**
     * 数据校验异常处理(用于application/json)
     *
     * @author LiuGangQiang Create in 2020/03/29
     * @param e {@link BindException}
     * @return {@link Result}
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> bindErrorHandler(MethodArgumentNotValidException e) {
        BindingResult result = e.getBindingResult();
        if (LOGGER.isWarnEnabled()) {
            LOGGER.warn("validator message [json] --> {}", result.getFieldError().getDefaultMessage());
        }
        return ResultBuilder.build(ResultStatus.BAD_REQUEST).setMsg(result.getFieldError().getDefaultMessage());
    }

    /**
     * 远程调用异常
     *
     * @author LiuGangQiang Create in 2021/08/11
     * @param e {@link FeignException}
     * @return {@link Result}
     */
    @ExceptionHandler(FeignException.class)
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    public Result<?> feignErrorHandler(FeignException e) {
        if (LOGGER.isWarnEnabled()) {
            LOGGER.warn("feign appear error message --> {}", e.getMessage());
        }
        return ResultBuilder.build(ResultStatus.FEIGN_FALLBACK);
    }

    /**
     * 自定义异常处理
     *
     * @author LiuGangQiang Create in 2020/03/02
     * @param e {@link BusinessException}
     * @return {@link Object} 对象
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Object defaultErrorHandler(BusinessException e, HandlerMethod method) {
        Locale locale = LocaleContextHolder.getLocale();
        /* 获取消息 */
        String message = Prompt.bundle(locale, e.getMessage(), e.getArgs());
        if (LOGGER.isErrorEnabled()) {
            LOGGER.error("system appear error message --> {}", message);
        }
        /* 如果是下载文件则返回 {@link ResponseEntity} */
        if (method.getMethod().getReturnType() == ResponseEntity.class) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        return ResultBuilder.build(ResultStatus.SYSTEM_ERROR).setMsg(message);
    }

    /**
     * 默认异常处理器
     *
     * @author LiuGangQiang Create in 2020/03/02
     * @param e {@link Exception}
     * @return {@link Object} 对象
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Object defaultErrorHandler(Exception e, HandlerMethod method) {
        e.printStackTrace();
        if (LOGGER.isErrorEnabled()) {
            LOGGER.error("system appear error message --> {}", e.toString());
        }
        /* 如果是下载文件则返回 {@link ResponseEntity} */
        if (method.getMethod().getReturnType() == ResponseEntity.class) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        return ResultBuilder.build(ResultStatus.SYSTEM_ERROR).setMsg(e.toString());
    }
}
